package main

import (
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-redis/redis"
	"time"
	"github.com/mattheath/base62"
)

const (
	//URLIDKEY 全局计数器自增id
	URLIDKEY="next.url.id"

	//ShortlinkKey 映射短地址和长地址之间的关系
	ShortlinkKey ="shortlink:%s:url"

	//URLHashKey 映射长地址hash的值到短地址之间的关系
	URLHashKey = "urlhash:%s:url"

	//ShortlinkDetailKey 短地址详细信息的key映射到短地址和详细信息
	ShortlinkDetailKey ="shortlink:%s:detail"
)

//RedisCli
type RedisCli struct {
	Cli *redis.Client
}


// URLDetail
type URLDetail struct {
	URL string `json:"url"`
	CreatedAt string `json:"created_at"`
	ExpirationInMinutes time.Duration `json:"expiration_in_minutes"`
}

// 初始化redis客户端
func NewRedisCli(addr string, passwd string, db int) *RedisCli  {

	c := redis.NewClient(&redis.Options{
		Addr:               addr,
		Password:           passwd,
		DB:                 db,
	})
	if _, err := c.Ping().Result(); err != nil {
		panic(err)
	}
	return &RedisCli{Cli:c}
}


// 长地址转化为短地址
func (r RedisCli) Shorten(url string, exp int64) (string, error) {
	//convert url to sha1 hash
	h := toSha1(url)

	// 判断这个url是否转换过了
	d, err := r.Cli.Get(fmt.Sprintf(URLHashKey, h)).Result()

	if err == redis.Nil {
		 // 值不存在，不需要做什么
	}else if err != nil {
		return "", err
	} else { // 过期了
		if d == "{}" {
			//过期了, nothing to do
		} else {
			return d, nil
		}
	}

	// 增加自增器
	err = r.Cli.Incr(URLIDKEY).Err()

	if err != nil {
		return "", err
	}

	// 把key转换为base62的编码
	id, err := r.Cli.Get(URLIDKEY).Int64()

	if err != nil {
		return "", err
	}
	eid := base62.EncodeInt64(id)

	// 短地址和url做映射
	err = r.Cli.Set(fmt.Sprintf(ShortlinkKey, eid), url, time.Minute*time.Duration(exp)).Err()
	if err != nil {
		return "", err
	}

	// 长地址的hash值和长地址做映射
	err = r.Cli.Set(fmt.Sprintf(URLHashKey, h), eid, time.Minute*time.Duration(exp)).Err()
	if err != nil {
		return "", err
	}

	//详细信息的映射
	detail, err := json.Marshal(
		&URLDetail{
			URL:                 url,
			CreatedAt:           time.Now().String(),
			ExpirationInMinutes: time.Duration(exp),
		})

	if err != nil {
		return "", err
	}

	// 把序列化后的detail信息传到redis中
	err = r.Cli.Set(fmt.Sprintf(ShortlinkDetailKey, eid), detail, time.Minute*time.Duration(exp)).Err()
	if err != nil {
		return "", err
	}

	// 返回短地址
	return eid, nil
}

// 获取短地址相关的详细信息
func (r RedisCli) ShortlinkInfo(eid string) (interface{}, error) {
	d, err := r.Cli.Get(fmt.Sprintf(ShortlinkKey, eid)).Result()
	if err == redis.Nil {
		return "", StatusError{404, errors.New("Unkown short URL")}
	} else if err != nil {
		return "", err
	} else {// 拿到消息了
		return d, nil
	}
}

// 转换为长地址
func (r RedisCli) Unshorten(eid string) (string, error) {
	url, err := r.Cli.Get(fmt.Sprintf(ShortlinkKey, eid)).Result()
	if err == redis.Nil {
		return "", StatusError{404, err}
	} else if err != nil{
		return "", err
	} else {
		return url, nil
	}
}


func toSha1(str string) string {
	var (
		sha = sha1.New()
	)
	return string(sha.Sum([]byte(str)))
}
